/** @file    NetworkManager.cpp
 *  @author  Alessandro Polo
 *  @version $Id: NetworkManager.cpp 2880 2010-08-09 03:17:19Z alex $
 *  @brief
 * File containing methods for the wosh::NetworkManager class.
 * The header for this class can be found in NetworkManager.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/
 
 #include <core/NetworkManager.h>
 #include <core/NetworkManagerHelpers.h>
 #include <core/ObjectAllocator.h>
 #include <core/ObjectFactory.h>
 #include <core/WoshKernel.h>
 #include <core/BusManager.h>
 #include <core/SecurityManager.h>
 #include <core/SystemError.h>
 #include <core/MethodsCommon.h>
 #include <core/MessageFilter.h>
 #include <core/MessageProcessingCommon.h>
 #include <core/Fact.h>
 #include <core/List.h>
 #include <core/Table.h>
 #include <core/Utilities.h>
 #include <core/Functional.h>
 #include <core/DataModels.h>

 
using namespace std;
namespace wosh {


// Gamma singleton! static reference is allocated and maintained by WoshKernel
extern "C" {
	NetworkManager* NetworkManager::getInstance() { 
		if ( WoshKernel::getInstance() == NULL ) return NULL;
		return &WoshKernel::getInstance()->network();
	}
}
 // register as abstract because it is a singleton:
 WOSH_REGISTER_INTERFACE(wosh::NetworkManager, "wosh::WoshModule", 0.01, _static_NetworkManager )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

NetworkManager::NetworkManager( const WoshKernel* parent )
	: WoshModule(parent, _NetworkManager_NAME, _NetworkManager_URI) {

Log.setLevel( LOG_VERBOSE );

	this->HostsAutoConnect = 1;
	this->hostTimeout = 120;

	this->HostsVirtualURI.setParent( Object::getURI() );
	this->HostsVirtualURI.setName( "Hosts" );
	this->HostsVirtualURI.registerGlobal();
	this->ChannelsVirtualURI.setParent( Object::getURI() );
	this->ChannelsVirtualURI.setName( "Channels" );
	this->ChannelsVirtualURI.registerGlobal();
	this->ProtocolsVirtualURI.setParent( Object::getURI() );
	this->ProtocolsVirtualURI.setName( "Protocols" );
	this->ProtocolsVirtualURI.registerGlobal();

	Log(LOG_DEBUG, " Initializing NetworkOutMessageHandler.." );
	this->outgoingMsgHandler = new NetworkOutMessageHandler(this);
	Log(LOG_DEBUG, " Initializing NetworkInMessageHandler.." );
	this->incomingMsgHandler = new NetworkInMessageHandler(this);
	Log(LOG_DEBUG, " Initializing NetworkManagerLooper.." );
	this->hostTimeOutLooper = new NetworkManagerLooper(this);

	Log(LOG_DEBUG, " Initializing Properties.." );
	Properties.set( _NetworkManager_KEY_Hosts_Count,		0, Permission( Permission::Read ) );
	Properties.set( _NetworkManager_KEY_Channels_Count,	0, Permission( Permission::Read ) );
	Properties.set( _NetworkManager_KEY_Protocols_Count,	0, Permission( Permission::Read ) );
	Properties.set( _NetworkManager_KEY_AutoConnect,	"Yes", Permission( Permission::RX, Permission::RX, Permission::Read ) );

	Log(LOG_DEBUG, " Registering Methods.." );
	MethodRequest* mmList = Methods.create<MethodRequest>( _METHOD_List, "List", Permission(Permission::RX) );
	mmList->setMethod( this, (MethodRequestPtr)&NetworkManager::mmDoList );

	MethodList* mmListHosts = Methods.create<MethodList>( _NetworkManager_METHOD_Hosts_list, "List Hosts", Permission::R_XR_XR__() );
	mmListHosts->setDataFieldExtractor( DataFieldExtractor<WoshHost>::createFor< DataAdapterMapT<string,WoshHost> >(this->Hosts, "Network Hosts") );

	MethodRetrieve* mmRetrieveHosts = Methods.create<MethodRetrieve>( _NetworkManager_METHOD_Hosts_retrieve, "Retrieve WoshHost(s)", Permission::R_XR_XR__() );
	mmRetrieveHosts->setItemExtractor( ObjectExtractor<WoshHost, const std::string&>::createFor< DataAdapterMapT<string, WoshHost> >(this->Hosts, &WoshHost::getName ) );

	MethodList* mmListNetworkChannels = Methods.create<MethodList>( _NetworkManager_METHOD_Channels_list, "List Channels", Permission::R_XR_XR__() );
	mmListNetworkChannels->setDataFieldExtractor( DataFieldExtractor<NetworkChannel>::createFor< DataAdapterMapT<long,NetworkChannel> >(this->Channels, "Network Channels") );

	MethodList* mmListNetworkProtocols = Methods.create<MethodList>( _NetworkManager_METHOD_Protocols_list, "List Protocols", Permission::R_XR_XR__() );
	mmListNetworkProtocols->setDataFieldExtractor( DataFieldExtractor<NetworkProtocol>::createFor< DataAdapterListT<NetworkProtocol> >(this->Protocols, "Network Protocols") );


	MethodRequest* mmListNeighbours = Methods.create<MethodRequest>( _NetworkManager_METHOD_Neighbours_list, "List Neighbours", Permission(Permission::RX) );
	mmListNeighbours->setMethod( this, (MethodRequestPtr)&NetworkManager::mmDoListNeighbours );
	MethodRequest* mmListNetworkTree = Methods.create<MethodRequest>( _NetworkManager_METHOD_NetworkTree_list, "List NetworkTree", Permission(Permission::RX) );
	mmListNetworkTree->setMethod( this, (MethodRequestPtr)&NetworkManager::mmDoListNetworkTree );
	MethodRequest* mmListRoutesStatic = Methods.create<MethodRequest>( _NetworkManager_METHOD_RoutesStatic_list, "List Static Routes", Permission(Permission::RX) );
	mmListRoutesStatic->setMethod( this, (MethodRequestPtr)&NetworkManager::mmDoListRoutesStatic );
	MethodRequest* mmListRoutesDynamic = Methods.create<MethodRequest>( _NetworkManager_METHOD_RoutesDynamic_list, "List Dynamic Routes", Permission(Permission::RX) );
	mmListRoutesDynamic->setMethod( this, (MethodRequestPtr)&NetworkManager::mmDoListRoutesDynamic );

	MethodRequest* mmCreateChannel = Methods.create<MethodRequest>( _NetworkManager_METHOD_CreateChannel, "Create Channel", Permission::R_XR_XR__() );
	mmCreateChannel->setMethod( this, (MethodRequestPtr)&NetworkManager::mmDoCreateChannel );

	MethodRequest* mmGetStats = Methods.create<MethodRequest>( _NetworkManager_METHOD_Stats_get, "Get Stats", Permission(Permission::RX) );
	mmGetStats->setMethod( this, (MethodRequestPtr)&NetworkManager::mmDoGetStats );
	MethodRequest* mmStatusSummary = Methods.create<MethodRequest>( _NetworkManager_METHOD_StatusSummary, "Status Summary", Permission(Permission::RX) );
	mmStatusSummary->setMethod( this, (MethodRequestPtr)&NetworkManager::mmDoStatusSummary );

	Log(LOG_VERBOSE, " Registering Self-Host.." );
	this->localHost = new WoshHost( WoshKernel::getKernelName() );
	localHost->setDiscoverSource( "" );
	localHost->setDiscoveryBindAddress( "" );
	localHost->setHopsCount(-1);
	localHost->setNotifyFrequency( 0 );
	localHost->setKernelState( (int)WoshKernel::getKernelState() );
	localHost->setPermission( Permission(Permission::Read) );
	localHost->setParentObject(this);
	localHost->getURI().setParent( this->HostsVirtualURI );
	localHost->getURI().setName( localHost->getName() );
	localHost->getURI().registerGlobal();
	Hosts.set( localHost->getName(), this->localHost );
	Properties.update( _NetworkManager_KEY_Hosts_Count, 1 );
 }

NetworkManager::~NetworkManager() {
	Log(LOG_VERBOSE, ":~NetworkManager() Destroying.." );

	Log(LOG_DEBUG, ":~NetworkManager() Freeing NetworkManagerLooper.." );
	delete this->hostTimeOutLooper; this->hostTimeOutLooper = NULL;
	Log(LOG_DEBUG, ":~NetworkManager() Freeing NetworkInMessageHandler.." );
	delete this->incomingMsgHandler; this->incomingMsgHandler = NULL;
	Log(LOG_DEBUG, ":~NetworkManager() Freeing NetworkOutMessageHandler.." );
	delete this->outgoingMsgHandler; this->outgoingMsgHandler = NULL;

	Log(LOG_DEBUG, ":~NetworkManager() Freeing Channels [%d]..", this->Channels.count() );
	this->Channels.clear();

	Log(LOG_DEBUG, ":~NetworkManager() Freeing Protocols [%d]..", this->Protocols.count() );
	this->Protocols.clear();

	Log(LOG_DEBUG, ":~NetworkManager() Freeing Hosts [%d]..", this->Hosts.count() );
	this->Hosts.transactionBeginWrite();
	this->localHost = NULL;
	this->Hosts.free();
	this->Hosts.transactionEnd();

	Log(LOG_VERBOSE, " Destroyed." );
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////// KERNEL EVENTS

WRESULT NetworkManager::onKernelInitializing() {
	WRESULT ret = WoshModule::onKernelInitializing();
	this->hostTimeOutLooper->startThread(10000); // synch
	this->outgoingMsgHandler->startThread(10000); // synch
	this->incomingMsgHandler->startThread(10000); // synch
	if ( Properties.exists(_NetworkManager_KEY_StaticRoutes) ) {
		vector<string> routes;
		string routesStr = Properties.getValueOf(_NetworkManager_KEY_StaticRoutes).toString();
		Utilities::splitString(routesStr, ";", routes, false, true);
		std::vector<std::string>::iterator it;
		this->staticRoutes.transactionBeginWrite();
		for ( it=routes.begin(); it!=routes.end(); ++it ) {
			vector<string> mapping;
			Utilities::splitString(*it, ":", mapping, false, true);
			if ( mapping.size() != 2 ) {
				continue;
			 }
			// target host => router
			this->staticRoutes.set(mapping.at(0), mapping.at(1));
		 }
		this->staticRoutes.transactionEnd();
	 }
	return ret;
 }

WRESULT NetworkManager::onKernelInitialized() {
	WRESULT ret = WoshModule::onKernelInitialized();
	if ( Properties.exists(_NetworkManager_KEY_AutoLoadProtocols) ) {
		vector<string> protos;
		string protoStr = Properties.getValueOf(_NetworkManager_KEY_AutoLoadProtocols).toString();
		Utilities::splitString(protoStr, ";", protos, false, true);
		WRESULT ret_c = WRET_OK;
		std::vector<std::string>::iterator it;
		for ( it=protos.begin(); it!=protos.end(); ++it ) {
			NetworkProtocol* net_proto = loadProtocol(*it);
			if ( net_proto == NULL ) {
				ret += WRET_ERR_PARAM;
				continue;
			 }
			ret_c = installProtocol(net_proto);
			ret += ret_c;
		 }
	 }
	// this may be moved in RUNNING (see below)
	if ( Properties.exists(_NetworkManager_KEY_ListenChannels) ) {
		vector<string> protos;
		string protoStr = Properties.getValueOf(_NetworkManager_KEY_ListenChannels).toString();
		Utilities::splitString(protoStr, ";", protos, false, true);
		WRESULT ret_c = WRET_OK;
		std::vector<std::string>::iterator it;
		for ( it=protos.begin(); it!=protos.end(); ++it ) {
			ret_c = createChannel(*it);
			ret += ret_c;
		 }
	 }
	return ret;
 }

WRESULT NetworkManager::onKernelStopped() {
	WRESULT ret = WRET_OK;

	this->hostTimeOutLooper->quitThread(10000);
	this->outgoingMsgHandler->quitThread(10000);
	this->incomingMsgHandler->quitThread(10000);

	ret += WoshModule::onKernelStopped();
	return ret;
 }

/////////////////////////////////////////////////////////////////////////////////////////////// KERNEL EVENTS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////// EVENTS

void NetworkManager::busMessage( const Message& message, const Bus* source ) {
	(void)source;
	if ( message.isEmpty() ) return;
	if ( !message.getContent()->isRequest() ) return;

	// manage messages for myself
	Message* reply = NULL;
	if ( MessageFilter::isMessageTarget(message, this) ) {
		reply = MPC_busMessageRequest( message, Methods, Log );
		if ( reply == NULL ) return;
		reply->setSource( this );
		BusCore.postMessage(reply);
		return;
	 }
	else if ( !message.getDestination().isChildOf(this->getURI(), false) )
		return;	

	string target_name = message.getDestination().getName();
	const Request* request = message.getContent()->asRequest();

	if ( target_name == "Hosts" ) {
		if ( request->getMethod() == _METHOD_List ) {
			Method* method = Methods.find(_NetworkManager_METHOD_Hosts_list);
			if ( method == NULL ) {
				Log(LOG_WARNING, ":busMessage() FAILED : Method '%s' not found", request->getMethod().c_str() );
				reply = message.replyTo( request->replyResponse(WRET_ERR_PARAM, "Method not found!") );
			 }
			else
				reply = method->exec( &message );
		 }
		else {
			Log(LOG_WARNING, ":busMessage() FAILED : Method '%s' not found", request->getMethod().c_str() );
			reply = message.replyTo( request->replyResponse(WRET_ERR_PARAM, "Method not found!") );
		 }
		reply->setSource( this );
		reply->setSource( this->HostsVirtualURI ); // overwrite uri!
	 }
	else if ( target_name == "Channels" ) {
		if ( request->getMethod() == _METHOD_List ) {
			Method* method = Methods.find(_NetworkManager_METHOD_Channels_list);
			if ( method == NULL ) {
				Log(LOG_WARNING, ":busMessage() FAILED : Method '%s' not found", request->getMethod().c_str() );
				reply = message.replyTo( request->replyResponse(WRET_ERR_PARAM, "Method not found!") );
			 }
			else
				reply = method->exec( &message );
		 }
		else {
			Log(LOG_WARNING, ":busMessage() FAILED : Method '%s' not found", request->getMethod().c_str() );
			reply = message.replyTo( request->replyResponse(WRET_ERR_PARAM, "Method not found!") );
		 }
		reply->setSource( this );
		reply->setSource( this->ChannelsVirtualURI ); // overwrite uri!
	 }
	else if ( target_name == "Protocols" ) {
		if ( request->getMethod() == _METHOD_List ) {
			Method* method = Methods.find(_NetworkManager_METHOD_Protocols_list);
			if ( method == NULL ) {
				Log(LOG_WARNING, ":busMessage() FAILED : Method '%s' not found", request->getMethod().c_str() );
				reply = message.replyTo( request->replyResponse(WRET_ERR_PARAM, "Method not found!") );
			 }
			else
				reply = method->exec( &message );
		 }
		else {
			Log(LOG_WARNING, ":busMessage() FAILED : Method '%s' not found", request->getMethod().c_str() );
			reply = message.replyTo( request->replyResponse(WRET_ERR_PARAM, "Method not found!") );
		 }
		reply->setSource( this );
		reply->setSource( this->ProtocolsVirtualURI ); // overwrite uri!
	 }

	if ( reply != NULL ) {
		BusCore.postMessage(reply);
		return;
	 }

//	if ( !message.getDestination().isChildOf(this->getURI(), false) )
//		return;
	if ( message.getDestination().getPath().size() < 2 )
		return;

	if ( message.getDestination().getPath().at(1) == "Channels" ) {
		Log(LOG_DEBUG, ":busMessage() = Message-Request for Child '%s'", target_name.c_str() );
		this->Channels.transactionBeginRead(); // following operation won't ever really add/delete a channel item
		MPC_mmDo_InContainer<NetworkChannel>(message, this->Channels, Log, BusCore);
		this->Channels.transactionEnd();
	 }
	else if ( message.getDestination().getPath().at(1) == "Protocols" ) {
		Log(LOG_DEBUG, ":busMessage() = Message-Request for Child '%s'", target_name.c_str() );
		this->Protocols.transactionBeginRead(); // following operation won't ever really add/delete a channel item
		MPC_mmDo_InContainer<NetworkProtocol>(message, this->Protocols, Log, BusCore);
		this->Protocols.transactionEnd();
	 }
	else if ( message.getDestination().getPath().at(1) == "Hosts" ) {
		Log(LOG_DEBUG, ":busMessage() = Message-Request for Child '%s'", target_name.c_str() );
//		this->Hosts.transactionBeginRead(); // following operation won't ever really add/delete an item
//		MPC_mmDo_SimpleObjectInContainer<WoshHost>(message, this->Hosts, Log, BusCore);
//		this->Hosts.transactionEnd();

		this->Hosts.transactionBeginWrite();
		WoshHost* host = this->Hosts.find(target_name);
		if ( host == NULL ) {
			this->Hosts.transactionEnd();
			Log(LOG_VERBOSE, ":busMessage() = Host-Child '%s' not found!", target_name.c_str() );
			return;
		 }
		busMessageHost_( message, host);//no mutex
		this->Hosts.transactionEnd();

	 }
 }

void NetworkManager::busMessageHost_( const Message& message, WoshHost* host ) { //no mutex
	if ( host == NULL ) return;
	// check permission
	const Permission* jobPerm = &((const WoshHost*)host)->getPermission();
	bool allowRead = SecurityManager::evalAuthorization( message.getSecurityToken(), jobPerm, Permission::Read );
	if ( !allowRead ) {
		Log(LOG_WARNING, ":busMessageHost() : Not allowed to read Host '%s' ", host->getName().c_str() );
		return;
	 }
	const Request* request = message.getContent()->asRequest();
	Response* response = NULL;
	if ( request->getMethod() == _METHOD_GetProperty ) {
		string key = request->getArgument().toString();
		Property* prop = host->getProperty(key);
		if ( prop == NULL ) {
			response = request->replyResponse(WRET_ERR_PARAM, "Property not found");
		 }
		response = request->replyResponse(WRET_OK, prop);
	 }
	else if ( request->getMethod() == _METHOD_SetProperty ) {
		response = request->replyResponse(WRET_ERR_ILLEGAL_USE);
	 }
	else if ( request->getMethod() == _METHOD_ListProperties ) {
		response = request->replyResponse(WRET_OK, host->getProperties() );
	 }
	else if ( request->getMethod() == _METHOD_ListMethods ) {
		List* methods = new List(7);
		methods->push_back<Variant>( _METHOD_GetProperty );
		methods->push_back<Variant>( _METHOD_SetProperty );
		methods->push_back<Variant>( _METHOD_ListProperties );
		methods->push_back<Variant>( _METHOD_ListMethods );
		methods->push_back<Variant>( _NetworkManager_METHOD_Host_connect );
		methods->push_back<Variant>( _NetworkManager_METHOD_Host_disconnect );
		methods->push_back<Variant>( _NetworkManager_METHOD_Host_ping );
		response = request->replyResponse(WRET_OK, methods);
	 }
	// ------------------------------------------------------------------------------------------------------- method: CONNECT
	else if ( request->getMethod() == _NetworkManager_METHOD_Host_connect ) {
		if ( host == this->localHost ) {
			response = request->replyResponse(WRET_ERR_ILLEGAL_USE, "Can't connect localhost to itself");
		 }
		else {
			WRESULT ret = WRET_OK;
			ret = autoConnect_( host->getName(), NULL);
			response = request->replyResponse(ret);
		 }
	 }
	// -------------------------------------------------------------------------------------------------------
	else {
		Log(LOG_WARNING, ":busMessageHost() FAILED : Method '%s' not found", request->getMethod().c_str() );
	 }
	if ( response == NULL ) {
		return;
	 }
	Message* reply = message.replyTo(response);
	reply->setSource( host );
	BusCore.postMessage(reply);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////// EVENTS

WoshHost* NetworkManager::getLocalWoshHost() {
	MutexLockerRead mL(this->Hosts.getMutex());
	return this->localHost->clone();
 }

WRESULT NetworkManager::updateWoshHost( const WoshHost* host ) {
	MutexLockerWrite mL(this->Hosts.getMutex());
	return _updateWoshHost_( host, true, false );
 }

WRESULT NetworkManager::_updateWoshHost_( const WoshHost* host, bool raise_event, bool never_connect, int hops ) {
 	if ( host == NULL ) return WRET_ERR_PARAM;
	// manage local host settings, updating few keys (only related to Discovery Service)
	if ( this->localHost != NULL && this->localHost->getName() == host->getName() ) {
		this->localHost->setPermission( this->getPermission(), Permission::Overwrite_All, false );
		this->localHost->setSeen();
		this->localHost->setSeenRemote( this->localHost->getSeen() );
		this->localHost->setDiscoverSource( host->getDiscoverySource() );
		this->localHost->setDiscoveryBindAddress( host->getDiscoveryBindAddress() );
		this->localHost->setNotifyFrequency( host->getNotifyFrequency() );
		Log(LOG_DEBUG, ":updateWoshHost() Updated Local-Host '%s'", host->getName().c_str() );
		return WRET_OK;
	} 
	bool host_added = false; // for the raised FACT
	WoshHost* local_wosh_host = NULL;
	// manage new and old hosts
	bool new_host = !Hosts.exists( host->getName() ); //no mux
	if ( new_host ) {
		Log(LOG_INFO, ":updateWoshHost(%s, %d,%d, %d) New Host Found", host->getName().c_str(), raise_event,never_connect, hops );

		// clone host, overwriting/fixing some fields
		local_wosh_host = host->clone();
		local_wosh_host->setParentObject(this);
		local_wosh_host->setPermission( this->getPermission(), Permission::Overwrite_All, false );
		local_wosh_host->getURI().setParent( this->HostsVirtualURI );
		local_wosh_host->getURI().setName( local_wosh_host->getName() );
		local_wosh_host->getURI().registerGlobal();

		Hosts.set( host->getName(), local_wosh_host );
		Properties.transactionBeginWrite();
		Properties.update( _NetworkManager_KEY_Hosts_Count, Hosts.size() );
		Properties.transactionEnd();

		host_added = true; // for the raised FACT
	 }
	else { // its a known host, usually nothing to do, but it may offer a new channel
		Log(LOG_INFO, ":updateWoshHost(%s, %d,%d, %d) Updated Host", host->getName().c_str(), raise_event,never_connect, hops );

		local_wosh_host = Hosts.find(host->getName());
		if ( local_wosh_host != NULL ) {
			// update (most) fields
			local_wosh_host->setKernelState( host->getKernelState() );
			local_wosh_host->setDiscoverSource( host->getDiscoverySource() );
			local_wosh_host->setDiscoveryBindAddress( host->getDiscoveryBindAddress() );
			local_wosh_host->setNotifyFrequency( host->getNotifyFrequency() );
			// smart set, will only add new protocols:
			local_wosh_host->setProtocols( host->getProtocols() );
			local_wosh_host->setNeighbours( host->getNeighbours() );
		 }
		else {
			Log(LOG_CRITICAL, ":updateWoshHost() Disaligned lookup @ '%s'", host->getName().c_str() );
			return WRET_ERR_INTERNAL;
		 }
	 }
	local_wosh_host->setSeen();
	if ( hops < -1 ) {
		if ( hops == -2 ) {
			if ( local_wosh_host->getHopsCount() == -1 ) {
				// means was not set.. so assume its a neighbour (99% it really is)
				Log(LOG_VERBOSE, ":updateWoshHost() Setting Default Hops count [0] for %s", host->getName().c_str() );
				local_wosh_host->setHopsCount(0);
			 }
		 }
		else
			Log(LOG_WARNING, ":updateWoshHost() Unknown Hops count [%d] of '%s'", hops, host->getName().c_str() );
	 }
	else if ( hops == 0 ) {
		// update host as neighbour
		this->localHost->getNeighbours()[local_wosh_host->getName()] = Utilities::std_time();
		local_wosh_host->setHopsCount(0);
	 }
	else {
		local_wosh_host->setHopsCount(hops);
	 }
	WRESULT ret = WRET_OK;
	if ( !never_connect ) {
		if ( this->HostsAutoConnect ) {
			ret = autoConnect_( host->getName(), new_host ); // no mux
		 }
	 }
	// when not raising events, we are done :)
	if ( !raise_event ) {
		return ret;
	 }
	// notify event to local kernel
	WoshHost* host_copy = NULL;
	Fact* fact = NULL;
	if ( local_wosh_host != NULL )
		host_copy = local_wosh_host->clone();

	if ( host_added )
		fact = new WoshHost_ListChanged(host_copy, WoshHost_ListChanged::EVENT_LISTITEM_ADDED);
	else
		fact = new WoshHost_ListChanged(host_copy, WoshHost_ListChanged::EVENT_LISTITEM_UPDATED);
	Message* msg_event = new Message(fact);
	msg_event->setDestinationBroadcast(false);
	msg_event->setDestinationBus(_Bus_ANY);
	signMessage(msg_event);
	BusCore.postMessage(msg_event);
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT NetworkManager::autoConnect_( const string& name, bool new_host ) { // no mutex on HOST
	WoshHost* host = Hosts.find(name);
	if ( host == NULL ) {
		SystemError::raise(this, Log, SystemError::WARNING, 0, "_autoConnect", "[%s, %d] FAILED: Host Not Found!", name.c_str(), (int)new_host );
		return WRET_ERR_PARAM;
	 }
	if ( host->isConnected() > 0 ) {
		// host seems connected, but could be disconnected/crashed.., verify that:
		if ( new_host || host->isTimedOut(0.5) ) {
			WRESULT ret = pingHost_(host, NULL);
			if ( WSUCCEEDED(ret) ) {
				Log(LOG_WARNING, ":_autoConnect(%s,%d) : Host Pinged, Already Connected, Skipping..", name.c_str(), (int)new_host );
				if ( !new_host ) {
					return WRET_OK;
				 }
			 }
		 }
		else
			return WRET_OK;
	 }
	this->Protocols.transactionBeginRead();
	std::vector<std::string> protocolsAvailable;
	NetworkProtocol* nProtocol = findBestProtocol_(host, protocolsAvailable);
	if ( nProtocol == NULL ) {
		this->Protocols.transactionEnd();
		SystemError::raise(this, Log, SystemError::WARNING, _NetworkManager_ERROR_autoconnect, "_autoConnect", "[%s, %d] FAILED Selecting NetworkProtocol! [%s]", name.c_str(), (int)new_host, Utilities::joinVector(host->getProtocols(), ";").c_str() );
		return WRET_ERR_PARAM;
	 }
	Log(LOG_VERBOSE, ":_autoConnect(%s,%d) : Selected '%s'", name.c_str(), (int)new_host, nProtocol->getName().c_str() );
	WRESULT ret = nProtocol->connectChannel(protocolsAvailable, 0); // ASYNCH!!
	// must be asynch because hosts, and many stuff are locked!
	this->Protocols.transactionEnd();
	return ret;
 }

NetworkProtocol* NetworkManager::findBestProtocol_( WoshHost* host, std::vector<std::string>& protocolsAvailable ) {
	// no mutex on HOST, Protocols
	bool s_found = false;
	tNetworkProtocolList::ConstIterator it;
	const tNetworkProtocolList::ConstIterator it_end = this->Protocols.end();
	for ( it=this->Protocols.begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		std::vector<std::string>::const_iterator it_p;
		for ( it_p=host->getProtocols().begin(); it_p!=host->getProtocols().end(); it_p++) {
			s_found = (*it)->supportProtocol(*it_p);
			if ( !s_found ) continue;
			protocolsAvailable.push_back(*it_p);
		 }
		if ( protocolsAvailable.empty() ) continue;
		return *it;
	 }
	return NULL;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT NetworkManager::removeHost_( WoshHost* host ) {
	if ( host == NULL ) return WRET_ERR_PARAM;
	if ( host == this->localHost ) return WRET_ERR_PARAM;
	Log(LOG_VERBOSE, ":removeHost_(%s) : Removing Host..",  host->getName().c_str() );
	vector<NetworkChannel*> channels;
	this->Channels.transactionBeginRead();
	WRESULT ret = getChannelsOf_( channels, host, true );
	if ( WSUCCEEDED(ret) && channels.size() > 0 ) {
		Log(LOG_INFO, ":removeHost_(%s) : Removing Channels [%d]",  host->getName().c_str(), channels.size() );
		std::vector<NetworkChannel*>::const_iterator it;
		for ( it=channels.begin(); it!=channels.end(); ++it ) {
			if ( *it == NULL ) continue;
			(*it)->setDeleteFlag(true); // ASYNCH
		}
	 }
	this->Channels.transactionEnd();
	WoshHost* host_copy = host->clone();
	this->Hosts.eraseByValue(host, true); // SYNCH
	Log(LOG_INFO, ":removeHost_() : Removed Host '%s'", host_copy->getName().c_str() );
	Fact* fact = new WoshHost_ListChanged(host_copy, WoshHost_ListChanged::EVENT_LISTITEM_REMOVED);
	Message* msg_event = new Message(fact);
	msg_event->setDestinationBroadcast(false);
	signMessage(msg_event);
	BusCore.postMessage(msg_event);
	return WRET_OK;
 }

WRESULT NetworkManager::removeChannel_( NetworkChannel* channel ) { // SYNCH
	if ( channel == NULL ) return WRET_ERR_PARAM;
	std::string name = channel->getName();
	long id = channel->getID();
	Log(LOG_VERBOSE, ":removeChannel_(%s) : Removing Channel#%ld ..",  name.c_str(), id );
	this->Channels.eraseByValue(channel, true);
	Log(LOG_INFO, ":removeChannel_() : Removed Channel '%s' #%ld",  name.c_str(), id );
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT NetworkManager::pingHost_( WoshHost* host, NetworkChannel* channel ) { // no mutex on host
	if ( host == NULL ) return WRET_ERR_PARAM;
	WRESULT ret = WRET_OK;
	if ( channel != NULL ) { // then mutex was locked for CHANNELS!
		ret = channel->ping();
		if ( WSUCCEEDED(ret) ) {
			Log(LOG_INFO, ":pingHost_(%s) : SUCCEDED Pinging Channel %s #%ld",  host->getName().c_str(), channel->getName().c_str(), channel->getID() );
		 }
		else {
			Log(LOG_CRITICAL, ":pingHost_(%s) : FAILED#%d Pinging Channel %s #%ld", host->getName().c_str(), ret, channel->getName().c_str(), channel->getID() );
		 }
		return ret;
	 }
	// ping ALL channels!
	vector<NetworkChannel*> chs;
	this->Channels.transactionBeginRead();
	ret = getChannelsOf_(chs, host, true); // no mux
	if ( WFAILED(ret) || chs.size() == 0 ) {
		this->Channels.transactionEnd();
		Log(LOG_WARNING, ":pingHost_(%s) : Error/No Channels to ping?",  host->getName().c_str() );
		return WRET_ERR_ILLEGAL_USE;
	 }
	Log(LOG_INFO, ":pingHost_(%s) : Pinging ALL Channels [%d]",  host->getName().c_str(), chs.size() );
	std::vector<NetworkChannel*>::const_iterator it;
	for ( it=chs.begin(); it!=chs.end(); ++it ) {
		if ( *it == NULL ) continue;
		ret += pingHost_(host, *it); // recursive
	}
	this->Channels.transactionEnd();
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
WRESULT NetworkManager::installProtocol( const string& protocol ) {
 }
*/
WRESULT NetworkManager::installProtocol( NetworkProtocol* channel_protocol ) {
	if ( channel_protocol == NULL ) return WRET_ERR_PARAM;
	Log(LOG_INFO, ":installProtocol(%s) : Registering Protocol..",  channel_protocol->getName().c_str() );

	this->Protocols.transactionBeginWrite();
	if ( this->Protocols.exists(channel_protocol) ) {
		this->Protocols.transactionEnd();
		SystemError::raise(this, Log, SystemError::CRITICAL, 0, "installProtocol", "FAILED %s already registered!", channel_protocol->getName().c_str() );
		return WRET_ERR_PARAM;
	 }
	channel_protocol->setPermission( this->getPermission(), Permission::Overwrite_All, true );
	channel_protocol->setParentObject(this);
	channel_protocol->setListener(this);
	channel_protocol->getURI().setParent( this->ProtocolsVirtualURI );
	channel_protocol->getURI().setName( channel_protocol->getName() );
	channel_protocol->getURI().registerGlobal();

	this->Protocols.push_back(channel_protocol);
	Properties.transactionBeginWrite();
	Properties.update( _NetworkManager_KEY_Protocols_Count, Protocols.size() );
	Properties.transactionEnd();

//	Log(LOG_INFO, ":installProtocol(%s) : Loading default settings..",  channel_protocol->getName().c_str() );
//	WoshKernel* kernel = WoshKernel::getInstance();
//	if ( kernel != NULL )
//		channel_protocol->getConfiguration().intersect( kernel->SettingsDefault.getSettingsOf( channel_protocol->getName() ), true, false );

	Fact* fact = new Fact( _NetworkManager_EVENT_NetworkProtocolInstalled, new Variant(channel_protocol->getName()));
	this->Protocols.transactionEnd();

	Message* msg_event = new Message(fact);
	msg_event->setDestinationBroadcast(false);
	signMessage(msg_event);
	BusCore.postMessage(msg_event);
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT NetworkManager::createChannel( const std::string& protocol_string ) {
	MutexLockerRead mL(Protocols.getMutex());
	tNetworkProtocolList::ConstIterator it;
	const tNetworkProtocolList::ConstIterator it_end = this->Protocols.end();
	for ( it=this->Protocols.begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		bool ch_found = (*it)->supportProtocol(protocol_string);
		if ( !ch_found ) continue;
		Log(LOG_VERBOSE, ":createChannel(%s): Found %s [%s]", protocol_string.c_str(), (*it)->getName().c_str(), (*it)->getClassName() );
		WRESULT ret = (*it)->listenChannel(protocol_string, 30000); // SYNCH 30 seconds
		if ( WSUCCEEDED(ret) )
			Log(LOG_INFO, ":createChannel(%s): Found %s, Listening succeded", protocol_string.c_str(), (*it)->getName().c_str(), ret );
		else
			SystemError::raise(this, Log, SystemError::CRITICAL, _NetworkManager_ERROR_channel_listen, "__new_channel_listen", "[%s] Found %s, but ERROR#%d listening!", protocol_string.c_str(), (*it)->getName().c_str(), ret );
		return ret;
	 }

	SystemError::raise(this, Log, SystemError::CRITICAL, _NetworkManager_ERROR_channel_listen, "__new_channel_listen", "[%s] FAILED. No Network Protocol found", protocol_string.c_str() );
	return WRET_ERR_PARAM;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

NetworkProtocol* NetworkManager::loadProtocol( const string& network_protocol, double version ) {
	Log(LOG_VERBOSE, ":loadProtocol() : Requesting ObjectFactory to allocate [%s, %g]..", network_protocol.c_str(), version );
	NetworkProtocol* netProto = ObjectFactory::createTypeOf<NetworkProtocol>(network_protocol, version);
	if ( netProto == NULL ) {
		SystemError::raise(this, Log, SystemError::CRITICAL, 0, "loadProtocol", "FAILED (NULL) creating Protocol %s!", network_protocol.c_str() );
		return NULL;
	 }
	netProto->setPermission( this->getPermission(), Permission::Overwrite_All, true );
	return netProto;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool NetworkManager::isHost( const string& wosh_hostname ) const {
	this->Channels.transactionBeginRead();
	tLongNetworkChannelMap::ConstIterator it;
	const tLongNetworkChannelMap::ConstIterator it_end = this->Channels.end();
	for ( it=this->Channels.begin(); it!=it_end; ++it ) {
		if ( it->second == NULL ) continue;
		if (  it->second->getRemoteWoshName() != wosh_hostname ) continue;
		this->Channels.transactionEnd();
		return true;
	 }
	this->Channels.transactionEnd();
	return false;
 }

WRESULT NetworkManager::getChannelsOf_( vector<NetworkChannel*>& channels, WoshHost* host, bool only_connected ) {
	if ( host == NULL ) return WRET_ERR_PARAM;
	tLongNetworkChannelMap::ConstIterator it;
	const tLongNetworkChannelMap::ConstIterator it_end = this->Channels.end();
	for ( it=this->Channels.begin(); it!=it_end; ++it ) {
		if ( it->second == NULL ) continue;
		if ( it->second->getRemoteWoshName() != host->getName() ) continue;
		if ( only_connected && it->second->isConnected() )
			channels.push_back(it->second);
	 }
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////// METHODS

Response* NetworkManager::mmDoStatusSummary( const Request* request ) {
	if ( request == NULL ) return NULL;
	string protocol = request->getArgument().toString();
	Log(LOG_VERBOSE, ":mmDoStatusSummary(%s)", protocol.c_str() );
	List* idl = new List();
	// connection status
	int connected = 0; int disconnected = 0;
	tLongNetworkChannelMap::ConstIterator it;
	const tLongNetworkChannelMap::ConstIterator it_end = this->Channels.end();
	for ( it=this->Channels.begin(); it!=it_end; ++it ) {
		if ( it->second == NULL ) continue;
		if ( !it->second->isConnected() ) {
			++disconnected;
			continue;
		 }
		++connected;
	 }
	if ( connected == 0 ) {
		if ( disconnected == 0 ) {
			idl->push_back<Variant>( "Disconnected" );
		 }
		else {
		 }
	 }
	else {
		idl->push_back<Variant>( Utilities::format("Connected [%d]", connected) );
	 }
	return request->replyResponse(WRET_OK, idl);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* NetworkManager::mmDoGetStats( const Request* request ) {
	if ( request == NULL ) return NULL;
	string option = request->getArgument().toString();
	Log(LOG_VERBOSE, ":mmDoGetStats(%s)", option.c_str() );
	Table* tbl = NULL;
	return request->replyResponse(WRET_OK, tbl);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* NetworkManager::mmDoCreateChannel( const Request* request ) {
	if ( request == NULL ) return NULL;
	string protocol = request->getArgument().toString();
	Log(LOG_VERBOSE, ":mmDoCreateChannel(%s)", protocol.c_str() );

	WRESULT ret = createChannel(protocol);
	return request->replyResponse(ret);
 }

Response* NetworkManager::mmDoList( const Request* request ) {
	if ( request == NULL ) return NULL;
	string option = request->getArgument().toString();
	Log(LOG_VERBOSE, ":mmDoListHost(%s)", option.c_str() );
	if ( option == "s" || option == "short" ) {
		List* objs = new List(3);
		objs->push_back<Variant>( "Hosts" );
		objs->push_back<Variant>( "Channels" );
		objs->push_back<Variant>( "Protocols" );
		return request->replyResponse(WRET_OK, objs);
	 }
	else {
		Table* tbl = new Table( 3, 3, true );
		tbl->setTableName("NetworkManager children");
		tbl->setHeaderHorizontal("Name", 0);
		tbl->setHeaderHorizontal("URI", 1);
		tbl->setHeaderHorizontal("Count", 2);
		unsigned int iRow = 0;
		tbl->set<Variant>( "Hosts", iRow, 0);
		tbl->set( this->HostsVirtualURI.clone(), iRow, 1);
		tbl->set<Variant>( this->Hosts.size(), iRow, 2);
		++iRow;
		tbl->set<Variant>( "Channels", iRow, 0);
		tbl->set( this->ChannelsVirtualURI.clone(), iRow, 1);
		tbl->set<Variant>( this->Channels.size(), iRow, 2);
		++iRow;
		tbl->set<Variant>( "Protocols", iRow, 0);
		tbl->set( this->ProtocolsVirtualURI.clone(), iRow, 1);
		tbl->set<Variant>( this->Protocols.size(), iRow, 2);
		++iRow;
		return request->replyResponse(WRET_OK, tbl);
	 }
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* NetworkManager::mmDoListNeighbours( const Request* request ) {
	if ( request == NULL ) return NULL;
	string option = request->getArgument().toString();
	Log(LOG_VERBOSE, ":mmDoListNeighbours(%s)", option.c_str() );
	if ( option == "s" || option == "short" ) {
		List* objs = new List();
		this->Hosts.transactionBeginRead();
		std::vector<string> names = this->localHost->getNeighboursName();
		std::vector<string>::const_iterator it;
		for ( it=names.begin(); it!=names.end(); ++it ) {
			objs->push_back<Variant>( *it );
		 }
		this->Hosts.transactionEnd();
		return request->replyResponse(WRET_OK, objs);
	 }
	else {
		this->Hosts.transactionBeginRead();
		Table* tbl = new Table( this->localHost->getNeighbours().size(), 2, true );
		tbl->setTableName("Hosts");
		tbl->setHeaderHorizontal("Name", 0);
		tbl->setHeaderHorizontal("TimeStamp", 1);
		unsigned int iRow = 0;
		std::map<std::string, long>::const_iterator it;
		for ( it=this->localHost->getNeighbours().begin(); it!=this->localHost->getNeighbours().end(); ++it ) {
			tbl->set<Variant>( it->first, iRow, 0);
			tbl->set( new Variant(it->second, Variant::DATETIME), iRow, 1);
			++iRow;
		 }
		this->Hosts.transactionEnd();
		return request->replyResponse(WRET_OK, tbl);
	 }
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* NetworkManager::mmDoListNetworkTree( const Request* request ) {
	if ( request == NULL ) return NULL;
	string option = request->getArgument().toString();
	Log(LOG_VERBOSE, ":mmListNetworkTree(%s)", option.c_str() );
	this->Hosts.transactionBeginRead();
	this->Hosts.transactionEnd();
	return request->replyResponse(WRET_OK);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* NetworkManager::mmDoListRoutesStatic( const Request* request ) {
	if ( request == NULL ) return NULL;
	string option = request->getArgument().toString();
	Log(LOG_VERBOSE, ":mmDoListRoutesStatic(%s)", option.c_str() );
	if ( option == "s" || option == "short" ) {
		this->staticRoutes.transactionBeginRead();
		List* objs = new List(this->staticRoutes.size());
		tStrStrMap::ConstIterator it;
		const tStrStrMap::ConstIterator it_end = this->staticRoutes.end();
		for ( it=this->staticRoutes.begin(); it!=it_end; ++it ) {
			objs->push_back<Variant>( new Variant(it->first) );
		 }
		this->staticRoutes.transactionEnd();
		return request->replyResponse(WRET_OK, objs);
	 }
	else {
		this->staticRoutes.transactionBeginRead();
		Table* tbl = new Table( this->staticRoutes.size(), 2, true );
		tbl->setTableName("Routes Static");
		tbl->setHeaderHorizontal("Target", 0);
		tbl->setHeaderHorizontal("Forwarder", 1);
		unsigned int iRow = 0;
		tStrStrMap::ConstIterator it;
		const tStrStrMap::ConstIterator it_end = this->staticRoutes.end();
		for ( it=this->staticRoutes.begin(); it!=it_end; ++it ) {
			tbl->set<Variant>( it->first, iRow, 0);
			tbl->set<Variant>( it->second, iRow, 1);
			++iRow;
		 }
		this->staticRoutes.transactionEnd();
		return request->replyResponse(WRET_OK, tbl);
	 }
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* NetworkManager::mmDoListRoutesDynamic( const Request* request ) {
	if ( request == NULL ) return NULL;
	string option = request->getArgument().toString();
	Log(LOG_VERBOSE, ":mmDoListRoutesDynamic(%s)", option.c_str() );
	if ( option == "s" || option == "short" ) {
		this->staticRoutes.transactionBeginRead();
		List* objs = new List(this->staticRoutes.size());
		tStrStrMap::ConstIterator it;
		const tStrStrMap::ConstIterator it_end = this->staticRoutes.end();
		for ( it=this->staticRoutes.begin(); it!=it_end; ++it ) {
			objs->push_back<Variant>( new Variant(it->first) );
		 }
		this->staticRoutes.transactionEnd();
		return request->replyResponse(WRET_OK, objs);
	 }
	else {
		this->staticRoutes.transactionBeginRead();
		Table* tbl = new Table( this->staticRoutes.size(), 2, true );
		tbl->setTableName("Routes Dynamic");
		tbl->setHeaderHorizontal("Target", 0);
		tbl->setHeaderHorizontal("Forwarder", 1);
		unsigned int iRow = 0;
		tStrStrMap::ConstIterator it;
		const tStrStrMap::ConstIterator it_end = this->staticRoutes.end();
		for ( it=this->staticRoutes.begin(); it!=it_end; ++it ) {
			tbl->set<Variant>( it->first, iRow, 0);
			tbl->set<Variant>( it->second, iRow, 1);
			++iRow;
		 }
		this->staticRoutes.transactionEnd();
		return request->replyResponse(WRET_OK, tbl);
	 }
 }

///////////////////////////////////////////////////////////////////////////////////////////////////// METHODS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////// STATIC METHODS

void NetworkManager::push_message( const Message* message, const Bus* source ) {

//	WRESULT validated = SecurityManager::validate( message, source );
//	if ( WFAILED(validated) ) {
//		Log(LOG_WARNING, ":channel_incomingMessage(%s) host:%s  msg-kernel:%s   REFUSED#%d", channel->getName().c_str(), channel->getRemoteWoshName().c_str(), message->getSourceKernel().c_str(), validated );
//		///@bug BUG TODO SECURITY!!
//	 }

	if ( NetworkManager::getInstance() == NULL ) return;
	if ( NetworkManager::getInstance()->outgoingMsgHandler == NULL ) return;
	NetworkManager::getInstance()->outgoingMsgHandler->busMessage( *message, source );
 }

void NetworkManager::push_to_BusManager( Message* message, NetworkChannel* channel) {
	BusManager::push_message(message, channel);
 }

////////////////////////////////////////////////////////////////////////////////////////////// STATIC METHODS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void NetworkManager::network_protocol_supported( const string& protocol_string, NetworkChannel* channel, NetworkProtocol* ) {
	Log(LOG_INFO, ":network_protocol_supported() Now supporting %s", protocol_string.c_str() );

	this->Hosts.transactionBeginWrite();
	this->localHost->setProtocolSupported(protocol_string); // will add when missing
	this->Hosts.transactionEnd();

	if ( channel == NULL  )
		return;

	channel->getURI().setParent( this->ChannelsVirtualURI );
	channel->getURI().setName( channel->getName() );
	channel->setPermission( this->getPermission(), Permission::Overwrite_All, true );
	channel->setListener(this);

	this->Channels.transactionBeginWrite();
	if ( this->Channels.exists(channel->getID()) ) {
		this->Channels.transactionEnd();
		return;
	 }
	this->Channels.set(channel->getID(), channel);
	Properties.transactionBeginWrite();
	Properties.update( _NetworkManager_KEY_Channels_Count, this->Channels.size() );
	Properties.transactionEnd();
	this->Channels.transactionEnd();
 }

void NetworkManager::network_protocol_new_channel( NetworkChannel* channel, short in_out, NetworkProtocol* ) {
	if ( channel == NULL ) {
		Log(LOG_WARNING, ":network_protocol_new_channel() NULL Channel!" );
		return;
	 }
	Log(LOG_INFO, ":network_protocol_new_channel(%s) ID#%ld [In/Out=%d]", channel->getName().c_str(), channel->getID(), in_out );

	channel->getURI().setParent( this->ChannelsVirtualURI );
	channel->getURI().setName( channel->getName() );
	channel->setPermission( this->getPermission(), Permission::Overwrite_All, true );
	channel->setListener(this);
	if ( in_out == 1 )// accept incoming connection
		channel->connect(); // SYNCH CONNECT

	this->Channels.transactionBeginWrite();
	if ( this->Channels.exists(channel->getID()) ) {
		this->Channels.transactionEnd();
		return;
	 }
	this->Channels.set(channel->getID(), channel);
	Properties.transactionBeginWrite();
	Properties.update( _NetworkManager_KEY_Channels_Count, this->Channels.size() );
	Properties.transactionEnd();
	this->Channels.transactionEnd();
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void NetworkManager::channel_incomingMessage( NetworkChannel* channel, Message* message ) {
	if ( channel == NULL || message == NULL ) return;

	WRESULT validated = SecurityManager::validate( message, channel );
	if ( WFAILED(validated) ) {
		Log(LOG_WARNING, ":channel_incomingMessage(%s) host:%s  msg-kernel:%s  REFUSED#%d", channel->getName().c_str(), channel->getRemoteWoshName().c_str(), message->getSourceKernel().c_str(), validated );
		///@bug BUG TODO SECURITY!!
	 }

	this->Hosts.transactionBeginWrite();
	WoshHost* my_host = this->Hosts.find(channel->getRemoteWoshName());
	if ( my_host != NULL ) {
		Log(LOG_VERBOSE, ":channel_incomingMessage(%s) host:%s  msg-kernel:%s", channel->getName().c_str(), channel->getRemoteWoshName().c_str(), message->getSourceKernel().c_str() );
		my_host->setSeen();
		if ( my_host->getName() == message->getSource().getKernelName() ) {
			my_host->setHopsCount(0);
			this->localHost->getNeighbours()[my_host->getName()] = Utilities::std_time();
		 }
	 }
	else {
		Log(LOG_WARNING, ":channel_incomingMessage(%s) : UNKNOWN host:%s ", channel->getName().c_str(), channel->getRemoteWoshName().c_str() );
	 }
	this->Hosts.transactionEnd();

	if ( message->hasNetworkForwarders() ) {
		this->Hosts.transactionBeginWrite();
		WoshHost* source_host = this->Hosts.find(message->getSource().getKernelName());
		if ( source_host != NULL ) {
			source_host->setHopsCount(message->getNetworkForwarders()->getHostCount());
		 }
		this->Hosts.transactionEnd();

		///@todo walk forwarders to re-built network tree
/*
		// avoid duplicate messages validating message (remote) timestamp against last seen timestamp (remote)
		///@bug won't work since timestamp is not THE SENDING TIME OF LAST HOP
		if ( my_host->getLastSeenRemote() > message->getNetworkForwarders()->getLastTimestamp() ) {
			Log(LOG_WARNING, ":channel_incomingMessage(%s) Message may be duplicate or out-of-date [host:%s]", channel->getName().c_str(), channel->getRemoteWoshName().c_str() );
		 }
		my_host->setSeenRemote( message->getNetworkForwarders()->getLastTimestamp() );
*/
	 }

	if ( !message->getDestination().isKernelAny() && !message->getDestination().isKernelLocal() ) {
		Log(LOG_VERBOSE, ":channel_incomingMessage(%s) Direct forwading [kernel:%s]", channel->getName().c_str(), message->getDestinationKernel().c_str() );
		// message for another kernel, direct forwading
		this->outgoingMsgHandler->busMessage( *message, NULL );
		delete message;
		return;
	 }

	this->incomingMsgHandler->pushMessage( message );
//	BusManager::push_message(message, channel);
 }

void NetworkManager::channel_Connected( NetworkChannel* channel, const WoshHost& host ) {
	if ( channel == NULL ) return;

	WRESULT validated = SecurityManager::validate( &host, NULL, channel );
	if ( WFAILED(validated) ) {
		Log(LOG_WARNING, ":channel_Connected(%s) Host: %s  REFUSED#%d [%s=%ld]", channel->getName().c_str(), host.getName().c_str(), validated, channel->getProtocol().c_str(), channel->getID() );
		///@bug BUG TODO SECURITY!!
	 }

	this->Hosts.transactionBeginWrite();
	if ( this->localHost != NULL )
		this->localHost->getConnections()[channel->getProtocol()] = channel->getID();

	WoshHost* my_host = this->Hosts.find(host.getName());
	if (my_host != NULL) {
		Log(LOG_INFO, ":channel_Connected(%s) Host: %s [%s=%ld]", channel->getName().c_str(), host.getName().c_str(), channel->getProtocol().c_str(), channel->getID() );

		my_host->setHopsCount(0);
		my_host->setSeen();
		my_host->getConnections()[channel->getProtocol()] = channel->getID();
		this->localHost->getNeighbours()[my_host->getName()] = Utilities::std_time();
		this->Hosts.transactionEnd();

		Message* msg_event = new Message( new WoshHost_ConnectionChanged(host.getName(), _WoshHost_Connected) ); // arg0 my_host->clone()
		msg_event->setDestinationBroadcast(false);
		signMessage(msg_event);
		BusCore.postMessage(msg_event);
	 }
	else {
		Log(LOG_INFO, ":channel_Connected(%s) Host: %s NOT Found, Inline Registering..!", channel->getName().c_str(), host.getName().c_str() );
		WRESULT ret = _updateWoshHost_( &host, true, true, 0 ); // raise event but DON'T AUTO-CONNECT
		this->Hosts.transactionEnd();
		if ( WFAILED(ret) )
			Log(LOG_CRITICAL, ":channel_Connected(%s) FAILED#%d Inline Registering of %s", channel->getName().c_str(), ret, host.getName().c_str() );
	 }
 }

void NetworkManager::channel_Disconnected( NetworkChannel* channel, const std::string& host_name ) {
	if ( channel == NULL ) return;
	channel->setDeleteFlag(true); // asynch removal

	this->Hosts.transactionBeginWrite();
	if ( this->localHost != NULL )
		this->localHost->getConnections()[channel->getProtocol()] = 0;

	WoshHost* host = Hosts.find(host_name);
	if (host != NULL) {
		Log(LOG_INFO, ":channel_Disconnected(%s) Host: %s [%s=0]", channel->getName().c_str(), host_name.c_str(), channel->getProtocol().c_str() );

		host->getConnections()[channel->getProtocol()] = 0;
		host->setSeen( -host->getSeen() );
		this->localHost->getNeighbours()[host_name] = 0;
		this->Hosts.transactionEnd();

		Message* msg_event = new Message( new WoshHost_ConnectionChanged(host_name,_WoshHost_Disconnected) ); // arg0: host->clone()
		msg_event->setDestinationBroadcast(false);
		signMessage(msg_event);
		BusCore.postMessage(msg_event);
	 }
	else {
		this->Hosts.transactionEnd();
		Log(LOG_WARNING, ":channel_Disconnected(%s) Host: '%s' NOT FOUND!", channel->getName().c_str(), host_name.c_str() );
	 }
 }

void NetworkManager::channel_Error( NetworkChannel* channel, int error_id, std::string error_description ) {
	if ( channel == NULL ) return;
	SystemError::raise(this, Log, SystemError::WARNING, _NetworkManager_ERROR_channel_error, "channel_Error", "[%s] Error#%d [%s]!", channel->getName().c_str(), error_id, error_description.c_str() );
	// disconnected is always raised by channel after this event.
	//channel_Disconnected(channel, channel->getRemoteWoshName());
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

}; // namespace wosh
